﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Input;
using EnvDTE;
using Microsoft.AspNet.Scaffolding;
using Poepon.AbpCodeGenerator.Core.UI;
using Poepon.AbpCodeGenerator.Core.Utils;

namespace Poepon.AbpCodeGenerator.Core.Scaffolders
{
    // 此类包含基架生成的所有步骤:
    // 1) ShowUIAndValidate() - 显示一个Visual Studio的对话框用于设置生成参数
    // 2) Validate() - 确认提取的Model   validates the model collected from the dialog
    // 3) GenerateCode() - 根据模板生成代码文件 if all goes well, generates the scaffolding output from the templates
    public class ModuleScaffolder : CodeGenerator
    {

        private GeneratorModuleViewModel _moduleViewModel;

        internal ModuleScaffolder(CodeGenerationContext context, CodeGeneratorInformation information)
            : base(context, information)
        {

        }

        public override bool ShowUIAndValidate()
        {
            _moduleViewModel = new GeneratorModuleViewModel(Context);

            GeneratorModuleDialog window = new GeneratorModuleDialog(_moduleViewModel);
            bool? isOk = window.ShowModal();

            if (isOk == true)
            {
                Validate();
            }
            return (isOk == true);
        }


        // Validates the model returned by the Visual Studio dialog.
        // We always force a Visual Studio build so we have a model
        private void Validate()
        {
            CodeType modelType = _moduleViewModel.ModelType.CodeType;

            if (modelType == null)
            {
                throw new InvalidOperationException("请选择一个有效的实体类。");
            }

            //var visualStudioUtils = new VisualStudioUtils();
            //visualStudioUtils.BuildProject(Context.ActiveProject);

        }
        private string GetEntityPrimaryKeyType(CodeType codeType)
        {
            string[] strBaseType = {
                "Abp.Domain.Entities.IEntity<System.Guid>",
                "Abp.Domain.Entities.IEntity<System.Int64>",
                "Abp.Domain.Entities.IEntity<System.DateTime>",
                "Abp.Domain.Entities.IEntity<System.Int16>",
                "Abp.Domain.Entities.IEntity<System.String>"
            };
            if (codeType.IsDerivedType("Abp.Domain.Entities.Entity") || codeType.IsDerivedType("Abp.Domain.Entities.IEntity") || codeType.IsDerivedType("Abp.Domain.Entities.IEntity<System.Int32>"))
            {
                return "int";
            }
            else if (codeType.IsDerivedType(strBaseType[0]))
            {
                return "Guid";
            }
            else if (codeType.IsDerivedType(strBaseType[1]))
            {
                return "long";
            }
            else if (codeType.IsDerivedType(strBaseType[2]))
            {
                return "DateTime";
            }
            else if (codeType.IsDerivedType(strBaseType[3]))
            {
                return "short";
            }
            else if (codeType.IsDerivedType(strBaseType[4]))
            {
                return "string";
            }
            else
            {
                return "int";
            }
        }
        public override void GenerateCode()
        {
            if (_moduleViewModel == null)
            {
                throw new InvalidOperationException("需要先调用ShowUIAndValidate方法。");
            }

            Cursor currentCursor = Mouse.OverrideCursor;
            try
            {
                Mouse.OverrideCursor = Cursors.Wait;
                var project = Context.ActiveProject;
                var entity = _moduleViewModel.ModelType.CodeType;

                var entityPrimaryKeyType = "int";
                entityPrimaryKeyType = GetEntityPrimaryKeyType(entity);
                var entityName = entity.Name;
                var entityNamespace = entity.Namespace.FullName;

                var projectNamespace = ProjectItemUtils.GetProjectName(entityNamespace);
                var functionName = _moduleViewModel.FunctionName;
                var pluralEntityName = VmUtils.ToPlural(entityName);
                var entityFolerName = ProjectItemUtils.GetModuleName(entityNamespace);
                var overwrite = _moduleViewModel.OverwriteFiles;

                Dictionary<string, object> templateParams = new Dictionary<string, object>()
                {
                    {"AppName", projectNamespace}
                    ,
                    {"EntityNamespace", entityNamespace}
                    ,
                    {"EntityName", entityName}
                    ,
                    {"EntityPrimaryKeyType", entityPrimaryKeyType}
                    ,
                    {"EntityFolerName", entityFolerName}
                      ,
                    {"PluralEntityName", pluralEntityName}
                    ,
                    {"FunctionName", functionName}
                    ,
                    {"DtoMetaTable", _moduleViewModel.DtoClassMetadataViewModel.DataModel}
                };

                var templates = new[]
                {
                    @"{AppName}.Application\{EntityFolerName}\Dto\{Entity}EditDto.cs",
                    @"{AppName}.Application\{EntityFolerName}\Dto\{Entity}ListDto.cs",
                    @"{AppName}.Application\{EntityFolerName}\Dto\CreateOrUpdate{Entity}Input.cs",
                    @"{AppName}.Application\{EntityFolerName}\Dto\Get{Entity}ForEditOutput.cs",
                    @"{AppName}.Application\{EntityFolerName}\Dto\Get{Entity}ListInput.cs",
                    @"{AppName}.Application\{EntityFolerName}\{Entity}AppService.cs",
                    @"{AppName}.Application\{EntityFolerName}\I{Entity}AppService.cs",
                    @"{AppName}.Common\{Entity}Common.cs",
                    @"{AppName}.Core\IRepositories\I{Entity}Repository.cs",
                    @"{AppName}.EntityFramework\EntityFramework\Repositories\{Entity}Repository.cs"
                };
                foreach (var template in templates)
                {
                    string outputPath = Path.GetFileNameWithoutExtension(Path.Combine(@"_GeneratedCode\", template.Replace("{AppName}", projectNamespace)
                        .Replace("{EntityFolerName}", entityFolerName)
                        .Replace("{Entity}", entityName)));

                    string templatePath = Path.Combine(template);

                    AddFileFromTemplate(project, outputPath, templatePath, templateParams, !overwrite);
                }
            }
            finally
            {
                Mouse.OverrideCursor = currentCursor;
            }
        }

        protected new bool AddFileFromTemplate(Project project, string outputPath, string templateName,
            IDictionary<string, object> templateParameters, bool skipIfExists)
        {
            if (project == null)
                throw new ArgumentNullException("project");
            if (templateParameters == null)
                throw new ArgumentNullException("templateParameters");
            var service1 = this.ServiceProvider.GetService<ICodeGeneratorFilesLocator>();
            var service2 = this.ServiceProvider.GetService<ICodeGeneratorActionsService>();
            string filePath = Path.GetFileNameWithoutExtension(templateName);
            string fileExtension = Path.GetExtension(templateName);

            string textTemplatePath = service1.GetTextTemplatePath(filePath, this.TemplateFolders, fileExtension);
            string str = Path.GetDirectoryName(project.DTE.Solution.FullName).TrimEnd('\\') + "\\";

            if (textTemplatePath.StartsWith(str, StringComparison.OrdinalIgnoreCase))
            {
                AddTelemetryData(this.Context, "MSInternal_CoreInfo", "IsCustomTemplate", true);
            }

            return service2.AddFileFromTemplate(project, outputPath, textTemplatePath, templateParameters, skipIfExists);
        }

        public static void AddTelemetryData(CodeGenerationContext context, string producerId, string key, object value)
        {
            if (context == null)
                throw new ArgumentNullException("context");
            if (producerId == null)
                throw new ArgumentNullException("producerId");
            if (key == null)
                throw new ArgumentNullException("key");
            if (value == null)
                throw new ArgumentNullException("value");
            GetItems(context, producerId)[key] = value;
        }

        private static Dictionary<string, object> GetItems(CodeGenerationContext context, string producerId)
        {
            Dictionary<string, object> property;
            if (!context.Items.TryGetProperty<Dictionary<string, object>>((object)producerId, out property))
            {
                property = new Dictionary<string, object>();
                context.Items.AddProperty((object)producerId, (object)property);
            }
            return property;
        }



        private void WriteLog(string str)
        {
            File.AppendAllText("D:\\Scaffolder.log.txt", str + "\r\n");
        }
        #region function library





        // Returns the relative path of the folder selected in Visual Studio or an empty 
        // string if no folder is selected.
        protected string GetSelectionRelativePath()
        {
            return Context.ActiveProjectItem == null ? String.Empty : ProjectItemUtils.GetProjectRelativePath(Context.ActiveProjectItem);
        }

        #endregion


    }
}
